#ifndef SERVER_H
#define SERVER_H

#include "../../Common.h"
#include "../../../GlobalClass.h"
#include "../IOCPDllClass/IOCP_Pleafles.h"

#include <QUuid>
//#include <QWidget>
//#include <QThreadPool>
#include <QQueue>
#include <QStack>
//#include <QWaitCondition>
//#include <windows.h>

/*
 * 鹰:和IOCP配合实现的C/S模型,包含了 加解密(Code控制),控制字符,包长度(粘包处理),群 等
 *
 * 流程:
 * 1.IOCP新建SocketContext,回调Fn_IOCP_SetSocketExtraData_Callback,新建一个_ClientInfo保存到IOCP的SocketExtraData中.
 * 2.IOCP接受Accept到新Socket后,先回调Fn_IOCP_Init_SocketExtraData_Callback,再进行Accept回调.
 * 3.发送数据时的Socket校验:
 *   由于使用池,所有的信息和数据不会释放,所以可以使用指针_ClientInfo进行传递和操作.
 *   若没有使用池,则发送时只能使用_SocketInfo::_SocketContext结构体进行传递和操作.
*/

class _ClientInfo;
class _ClientsGroupInfo;
class IOCP_Pleafles;


// 设置两个创建和释放SocketExtraData的回调函数(池!)
typedef void* (FUNCTION_CALL_TYPE *_SetGroupExtraDataCallback)();
typedef void (FUNCTION_CALL_TYPE *_UnsetGroupExtraDataCallback)(void *pGroupExtraData);
typedef void (FUNCTION_CALL_TYPE *_InitGroupExtraDataCallback)(void *pGroupExtraData);
typedef void (FUNCTION_CALL_TYPE *_ReleaseGroupExtraDataCallback)(void *pGroupExtraData);


//一个 群 数据
//池的一个基节点，会自动调用其init和release函数
//和GameServer的关系：GameServer定义回调函数，然后赋值给这里
class _ClientsGroupInfo
{
    //Q_FLAGS(_GroupStatusFlags)
public:
    //房间状态
    enum _GroupStatusFlag
    {
        Status_Normal = 0, //正常,(可加入)
        Status_Locked = 1,  //锁,(不可加入)
        Status_Playing = 2,  //正在玩
        Status_AutoClose = 4,  //无人自动关闭
    };
    Q_DECLARE_FLAGS(_GroupStatusFlags, _GroupStatusFlag)        //写在和E1同命名空间下(或同一类中)


    //房间基础数据
    struct _GroupInfo
    {
        qint32 nClientsMaxCount;   //最多玩家数
        qint32 nClientsWatchingMaxCount;   //最多观看数
        QString strPassword;    //密码
    };

    struct _RandomNumber
    {
        _RandomNumber(): m_nRandomNumberID(0)
        {}

        qint32 getRandomNumber()
        {
            if(m_listRandomNumber.isEmpty())
                return -1;

            m_nUsePoint++;
            if(m_nUsePoint < 0)
                return -2;
            if(m_nUsePoint >= m_listRandomNumber.count())
                m_nUsePoint = 0;
            return m_listRandomNumber.at(m_nUsePoint);
        }

        //创建n个随机数
        void createRandomNumber(qint32 n)
        {
            m_nRandomNumberID ++;
            m_listRandomNumber.clear();
            m_nUsePoint = -1;
            for(; n  > 0; n--)
            {
                m_listRandomNumber << intRand();
            }
        }

        void release()
        {
            //m_nRandomNumberID = 0;
            m_listRandomNumber.clear();
        }


        qint32 m_nRandomNumberID;
        QList<qint32> m_listRandomNumber;
        qint32 m_nUsePoint;

    }m_RandomNumber;


public:
    explicit _ClientsGroupInfo(IOCP_Pleafles *iocp = nullptr) :
        m_pIOCP(iocp)
    {
        m_uuid = QUuid::createUuid();

        if(SetGroupExtraDataCallback == nullptr)
            m_pClientsGroupExtraData = nullptr;
        else
            m_pClientsGroupExtraData = SetGroupExtraDataCallback();


        m_flagStatus = Status_AutoClose;
        m_GroupInfo.nClientsMaxCount = 0;
        m_GroupInfo.nClientsWatchingMaxCount = 0;
        m_pGroupMaster = nullptr;
        //m_GroupInfo.strPassword.clear();


        //_pool_init();
    }
    virtual ~_ClientsGroupInfo()
    {
        if(UnsetGroupExtraDataCallback != nullptr)
            UnsetGroupExtraDataCallback(m_pClientsGroupExtraData);
        UnsetGroupExtraDataCallback = nullptr;
        //_pool_release();
    }

//池需要调用的函数
    virtual void _pool_init()
    {
        if(InitGroupExtraDataCallback != nullptr)
            InitGroupExtraDataCallback(m_pClientsGroupExtraData);
    }
    virtual void _pool_release()
    {
        m_flagStatus = Status_AutoClose;
        m_pGroupMaster = nullptr;
        m_GroupInfo.nClientsMaxCount = 0;
        m_GroupInfo.nClientsWatchingMaxCount = 0;
        m_mapAllClients.clear();
        m_mapPlayingClients.clear();
        m_mapWatchingClients.clear();
        m_GroupInfo.strPassword.clear();
        m_stackClientIndex.clear();
        m_mapDisconnectClientData.clear();
        m_RandomNumber.release();

        if(ReleaseGroupExtraDataCallback != nullptr)
            ReleaseGroupExtraDataCallback(m_pClientsGroupExtraData);
    }

public:
    //改变房主
    //-1为随机,-2为取消,0~n为Group客户列表下标
    //返回0:取消房主;-1:错误(没有Client);1:设置成功
    int ChangeGroupMaster(int index)
    {
        int len = m_mapPlayingClients.count();
        if(len == 0)
            return -1;

        if(index == -2)    //取消
        {
            m_pGroupMaster = nullptr;
            return 0;
        }
        else if(index >= len)
            return -2;

        else if(index == -1) //随机
        {
            index = Random(0, len);
            ////m_pGroupMaster = m_listClients.at(Random(0, len));
            ////return 1;
        }

        if(index == 0)
        {
            m_pGroupMaster = m_mapPlayingClients.first();
            return 1;
        }
        else if(index == len - 1)
        {
            m_pGroupMaster = m_mapPlayingClients.last();
            return 1;
        }
        /*else if(index >= 0 && index < len)
        {
            m_pGroupMaster = m_listClients.at(index);
            return 1;
        }*/


        QMap<qint32, _ClientInfo*>::const_iterator ii;
        for(ii = m_mapPlayingClients.constBegin(); ii != m_mapPlayingClients.constEnd(); ++ii)
        {
            if(index == 0)
                break;
            index--;
        }

        if(ii == m_mapPlayingClients.constEnd())
            return -3;

        m_pGroupMaster = (*ii);

        return 1;
    }


    int ReJoinRunningGroup(_ClientInfo* pClientInfo, qint32 groupIndex);    //QUuid id)

    //释放某个已经掉线的座位（只在m_mapDisconnectClientData中查找）
    //返回
    QVariant ReleaseGroupIndex(qint32 groupIndex)
    {
        QVariant data;
        if(!m_mapDisconnectClientData.contains(groupIndex))
            qCritical() << "[!Server]ERROR:ReleaseGroupIndex:" << groupIndex;
        else
            data = m_mapDisconnectClientData.take(groupIndex);
        if(m_stackClientIndex.contains(groupIndex))
        {
            qCritical() << "[!Server]ERROR:ReleaseGroupIndex1:" << groupIndex;
            return -1;
        }
        m_stackClientIndex.push(groupIndex);

        return data;
    }



public:
    //发送 数据 给 组里 其他 所有客户,除了pClientInfo
    //type:-1(给全部客户,除了pClientInfo);0(给观看者,除了pClientInfo);1(给玩家,除了pClient)
    int SendToGroupAllClients(QByteArray ba, _ClientInfo *pClientInfo = nullptr, int type = -1);


//数据成员
public:
    IOCP_Pleafles       *m_pIOCP;     //IOCP指针,用到Send函数

    QMap<QUuid,_ClientsGroupInfo*>  *m_mapCanJoinGroups;    //正在做

public:
    QUuid               m_uuid;                 //房间ID(唯一性)

    _GroupStatusFlags   m_flagStatus;           //房间状态

    _GroupInfo          m_GroupInfo;            //房间信息

    _ClientInfo        *m_pGroupMaster;         //房主


    QMap<qint32, _ClientInfo*> m_mapAllClients;                 //所有客户列表,用SocketID作为 Key
    QMap<qint32, _ClientInfo*> m_mapPlayingClients;             //正在玩客户列表,用SocketID作为 Key
    QMap<qint32, _ClientInfo*> m_mapWatchingClients;            //观看客户列表,用SocketID作为 Key
    QStack<qint32>      m_stackClientIndex;     //座位号(每个新加入的Client分配1个从0到nClientsMaxCount的不重复数字)
    //QList<QSharedPointer<_ClientInfo>> m_listClients;

    //断网客户的数据保存（目前存的是m_unUserGameID）
    QMap<qint32, QVariant>      m_mapDisconnectClientData;           //GroupIndex


    //群的 其他数据 指针
    void                *m_pClientsGroupExtraData;



    //群的池操作,回调函数，需要赋值，自动去调用
    //这4个函数是1个线程(主线程)调用的
    static _SetGroupExtraDataCallback SetGroupExtraDataCallback;
    static _UnsetGroupExtraDataCallback UnsetGroupExtraDataCallback;
    static _InitGroupExtraDataCallback InitGroupExtraDataCallback;
    static _ReleaseGroupExtraDataCallback ReleaseGroupExtraDataCallback;
};

QDataStream& operator<<(QDataStream &ds, const _ClientsGroupInfo::_RandomNumber &r);
QDataStream& operator>>(QDataStream &ds, _ClientsGroupInfo::_RandomNumber &r);



/*
//每个SocketInfo
struct _SocketInfo {
    QList<_SocketInfo*>::Iterator pos;   //链表中的地址
    void *pSocket;
    void *pData;    //用户数据
};*/

//服务端 的 客户数据,二次封装(加入群,状态,控制字符,数据包扩展,在线统计等)
//IOCP的SocketExtraData指针的数据
//创建和获取这个类的对象，需要赋值IOCP的回调函数，IOCP的池自动调用
//实际情况：在GameServer中定义各种回调函数1和Server中各种回调函数2，然后在回调函数1的new、delete、调用init和release调用回调函数2，然后把回调函数1赋值给IOCP，相当于将Server的功能扩展了
struct _ClientInfo : public _SocketInfo
{
public:
    explicit _ClientInfo() :
        m_ucInControlByte(0),
        m_nStep(0),
        //transfersCode(-1),
        m_pGroup(nullptr),
        m_ClientGroupStatus(_ClientGroupStatus::GroupStatus_Nothing),
        m_nIndex(-1),
        m_nClientType(0),
        m_pClientExtraData(nullptr)
    {
        //_pool_init();
    }
    virtual ~_ClientInfo()
    {
        //if(m_pClientExtraData != nullptr)
        //    delete m_pClientExtraData;

        m_pClientExtraData = nullptr;
        //_pool_release();
    }

    //池节点初始化(然后保存到队列中供调用)
    virtual void _pool_init() Q_DECL_OVERRIDE
    {
        //放在最前，有可能派生类需要用到基类的数据
        _SocketInfo::_pool_init();


        m_dtLogin = QDateTime::currentDateTime();
        m_bDisconnected = false;
        //qDebug() << "[Server]Server _ClientInfo _pool_init";
    }

    //池节点释放(然后保存到队列中供调用)
    virtual void _pool_release() Q_DECL_OVERRIDE
    {
        m_listInCodeData.clear();
        m_listInTransfersCode.clear();  //接受加密包时的code

        m_ucInControlByte = 0;
        m_nStep = 0;
        m_pGroup = nullptr;
        m_ClientGroupStatus = _ClientGroupStatus::GroupStatus_Nothing;
        m_nIndex = -1;

        m_nClientType = 0;
        m_dtLogout = QDateTime::currentDateTime();
        m_bDisconnected = true;
        //qDebug() << "[Server]Server _ClientInfo _pool_release";


        //放在最后，有可能派生类还在引用基类的数据
        _SocketInfo::_pool_release();
    }



public:
    //切换用户ready状态:GroupStatus_Waiting和GroupStatus_Readying切换返回0,其他返回-1
    Q_INVOKABLE int ChangeGroupStatus()
    {
        if(m_ClientGroupStatus == _ClientGroupStatus::GroupStatus_Waiting)
            ChangeGroupStatus(_ClientGroupStatus::GroupStatus_Readying);
        else if(m_ClientGroupStatus == _ClientGroupStatus::GroupStatus_Readying)
            ChangeGroupStatus(_ClientGroupStatus::GroupStatus_Waiting);
        else
            return -1;
        return 0;
    }
    Q_INVOKABLE void ChangeGroupStatus(_ClientGroupStatus status)
    {
        m_ClientGroupStatus = status;
    }



public:
    //unsigned long ulRecivedSize;    //已接受包的长度(不含数据)

    quint8              m_ucInControlByte;               //数据包的控制字符
    int                 m_nStep;                           //网络包识别时的 第几阶段

    QList<QByteArray>   m_listInCodeData;       //要解密的包队列（用不到，因为加密必须要有Code，对应解密也有Code）
    QList<qint16>       m_listInTransfersCode;  //接受加密包时的code


    _ClientsGroupInfo   *m_pGroup;                  //加入的房间
    _ClientGroupStatus   m_ClientGroupStatus;       //玩家房间内状态
    qint32               m_nIndex;                  //座位号(房间内的唯一ID,0~MaxClient)

    //_ClientStatus clientStatus;

    //是否断线
    bool                m_bDisconnected;
    qint16              m_nClientType;          //客户端类型
    QDateTime           m_dtLogin;              //登录时间
    QDateTime           m_dtLogout;             //登出时间

    //额外的数据(由用户去定义,创建,删除)
    void                *m_pClientExtraData;    //这个也作为 池数据,所以不能再_release中置nullptr!!
};




//二次封装,加入日志,群,配置,将IOCP功能扩展(重写回调函数和信号)
class Server : public QObject
{
    Q_OBJECT

public:
    explicit Server(QObject *parent = nullptr);
    virtual ~Server();

//IOCP的4个线程发送的5个信号(可在使用前绑定Debug函数)
signals:
    void s_Info(QString msg);
    void s_Debug(QString msg);
    void s_Warning(QString msg);
    void s_Critical(QString msg);

    void s_Accept(_SocketInfo::_SocketContext socketContext);
    void s_Disconnect(int type, _SocketInfo::_SocketContext socketContext);
    void s_SendFinished(void*buf, unsigned long buflen, _SocketInfo::_SocketContext socketContext);
    void s_ReadyRead(QByteArray baData, quint8 controlByte, _SocketInfo::_SocketContext socketContext);


//群函数,只是操作,并不向玩家发送数据,没有重载,可单独调用!!
public:
    //找到房间,没有返回nullptr
    _ClientsGroupInfo* GetGroup(QUuid id);

    //找房间，除了pClientInfo->pGroup
    //随机开始找
    _ClientsGroupInfo* SearchGroup(_ClientInfo* pClientInfo, QString password);

    //找房间，除了pClientInfo->pGroup
    //从头开始找
    _ClientsGroupInfo* SearchGroup1(_ClientInfo* pClientInfo, QString password);

    //创建一个房间
    //参数:最大玩家;最多观看者;密码
    _ClientsGroupInfo* CreateGroup(qint32 clientMaxCount = 2, qint32 clientWatchingMaxCount = -1, QString password = "");

    //加入pGroup房间,如果为nullptr则随机加入
    //0为成功,<0各种错误:-1已经加入群组;-2所有房间满;-3房间状态不对;-4指针空
    //int JoinGroup(_ClientInfo* pClientInfo, _ClientsGroupInfo *pGroup, const QString& password = "");

    //Client加入房间id，password为密码;
    //id为QUuid的Null，则随机加入一个无密码的、没锁的、没满的Group；
    //joinType为0,表示加入玩家组,为1加入观看组;
    //返回值<0各种错误；
    int JoinGroup(_ClientInfo* pClientInfo, QUuid id, const QString& password = "", int joinType = 0);


    //pClientInfo退出房间,不解散群
    //bKeepGroupIndex 标识是否保持GroupIndex（断线重连），为true则不释放index
    //ret>=0群剩余客户,<0出错
    //如果用户是群主,换群主
    int ExitGroup(_ClientInfo* pClientInfo, bool bKeepGroupIndex = false, QVariant data = QVariant());

    //关闭房间,处理房间内玩家数据
    //返回0正常,<0出错
    int CloseGroup(_ClientsGroupInfo *pGroup);


    //给sock发送所有房间的信息
    //bool SendAllGroupsInfoToClient(_ClientInfo* pClientInfo);

    //给所有玩家发送 sockGroup的id和玩家数的更新信息
    //bool SendAllGroupsInfoToAllClients(SocketGroup* sockGroup, int type);



public://!!!
    void sendGroupInfoToAllClients(_ClientsGroupInfo *pGroup){} //给所有Clients发送pGroup信息(新建)
    void sendGroupInfoToAllClients(QUuid uuid, int type = 0){}  //同上,type:1:关闭
    void sendClientJoinInfoToAllClients(_ClientInfo *pClientData){} //给所有Clients发送pClientData的Join信息

    void sendClientReadyInfoToAllClients(_ClientInfo *pClientData){} //给所有Clients发送pClientData的Ready状态
    void sendToAllClientInGroup(QByteArray ba, _ClientInfo *pClientData){}   //给Group中其他Client发送ba




//其他(外部调用):
    //显示信息
    /*
    virtual void Message(QString msg)
    {
        //qDebug()<<"ThreadId:"<<::GetCurrentThreadId();
        qDebug() << msg;
    }
    //调试信息,写入日志
    virtual void Debug(QString msg)
    {
        //qDebug()<<"ThreadId:"<<::GetCurrentThreadId();
        / *if(m_fileLog.isOpen())
        {
            m_fileLogStream << msg << "\r\n";
            m_fileLog.flush();
        }* /
        qDebug() << msg;
    }
    virtual void Critical(QString msg)
    {
        qCritical() << msg;
    }*/



    virtual bool OpenLog(QString filename = "ServerLog.txt")
    {
        m_fileLog.setFileName(filename);
        return m_fileLog.open(QIODevice::Append | QIODevice::Text);
    }
    virtual void CloseLog()
    {
        m_fileLog.close();
    }



    //创建1个Code并发送(0~10000)
    bool CreateTransmitsCodeAndSend(_ClientInfo *pClientInfo, int count = 1);

    //将baData使用baCode解密!
    //返回true为解密成功
    bool RecieveUseTransfersCode(_ClientInfo *pClientInfo, QByteArray &baData);

public:
    bool Start()
    {
        m_nAcceptedCount = 0;
        if(OpenLog() == false)
            return false;
        return m_IOCPModel.Start();
    }

    void Stop()
    {
        m_IOCPModel.Stop();
        //CloseLog();
    }

    void __StopCallback()
    {
        CloseLog();
        m_poolClientsGroupInfo.clearEmptyList();
        m_poolClientsGroupInfo.clearUsing();
        m_mapGroups.clear();
        m_mapAllClients.clear();
        m_IOCPModel.StopCallback();
    }


    int onAccept(_SocketInfo::_SocketContext socketContext);
    int onDisconnect(int type, _SocketInfo::_SocketContext socketContext);
/*
    int Start()
    {
    //IOCP服务启动
        m_IOCPModel.init();

        m_IOCPModel.SetIOCPMessageCallback((void*)Fn_IOCP_Message_Callback,this);
        m_IOCPModel.SetIOCPDbgMessageCallback((void*)Fn_IOCP_MessageDbg_Callback,this);
        m_IOCPModel.SetIOCPAcceptCallback((void*)Fn_IOCP_Accept_Callback,this);
        m_IOCPModel.SetIOCPRecvCallback((void*)Fn_IOCP_Recv_Callback,this);
        m_IOCPModel.SetIOCPCloseCallback((void*)Fn_IOCP_Close_Callback,this);

        m_IOCPModel.SetServerIP("0.0.0.0");
        m_IOCPModel.SetServerPort(8806);

        m_IOCPModel.Start();

    //数据库连接
        db = QSqlDatabase::addDatabase("QMYSQL","mysql");
        db.setHostName("leamus.gotoftp2.com");
        db.setDatabaseName("leamus");
        db.setUserName("leamus");
        db.setPassword("19880723");
        if(!db.open())
        {
            Debug(QString("Database Connection ERROR:%1").arg(db.lastError().text()));
        }
        else
        {
            Debug(QString("Database Connection Successful"));
        }
        return 0;
    }
    void Stop()
    {
        m_IOCPModel.Stop();
        if(db.isValid() && db.isOpen())
            db.close();
    }
*/



//IOCP回调函数
public:
    //注意:这些都是IOCP工作线程的回调函数,不可以直接操作主线程UI!!!
    //将 列表操作 放入主线程,就不需要锁了
    //不需要再重写这些回调函数了，直接接收信号即可
    static void FUNCTION_CALL_TYPE Fn_IOCP_Message_Callback(int level, const char *msg, void *param);

    //static void FUNCTION_CALL_TYPE Fn_IOCP_MessageDbg_Callback(const char *msg,void *param);

    //Accept的池 实现
    static void FUNCTION_CALL_TYPE Fn_IOCP_Accept_Callback(int socketID,void *pIOCPSocketContext,void *param);

    //Accept的 非池 实现
    static void FUNCTION_CALL_TYPE Fn_IOCP_Accept_Callback_Non_Pool(int socketID,void *pIOCPSocketContext,void *param);

    //type是断开类型
    static void FUNCTION_CALL_TYPE Fn_IOCP_Close_Callback(int type, void *pIOCPSocketContext, void *param);

    static void FUNCTION_CALL_TYPE Fn_IOCP_Recv_Callback(void*buf,unsigned long buflen,void *pIOCPSocketContext,void*param);

    static void FUNCTION_CALL_TYPE Fn_IOCP_Send_Callback(void*buf, unsigned long buflen, unsigned long bufTransfered, void*pIOCPSocketContext, void*param);


//4个SocketExtraData回调(IOCP回调)
//注意:这4个函数是IOCP回调的,除了Unset是1个线程(主线程)调用外,其他3个都会在不同的线程内调用!!!所以注意内容
    static void* FUNCTION_CALL_TYPE Fn_IOCP_SetSocketExtraData_Callback();

    static void FUNCTION_CALL_TYPE Fn_IOCP_UnsetSocketExtraData_Callback(void *pExtraData);

    static void FUNCTION_CALL_TYPE Fn_IOCP_Init_SocketExtraData_Callback(void *pExtraData);

    static void FUNCTION_CALL_TYPE Fn_IOCP_Release_SocketExtraData_Callback(void *pExtraData);




//数据成员
public:
    IOCP_Pleafles           m_IOCPModel;            //IOCP模型对象
    quint32                 m_nRecievedDataMaxSize; //一个包最大的数据长度,大于时长度非法并丢弃(为0不限制)
    quint32                 m_nDataCacheMaxSize;    //最大 接受数据缓存的个数(超过会释放无用数据)
    quint32                 m_nAcceptedCount;       //统计总共连接次数
    quint32                 m_nTransmitsCodeCount;  //一次发送多少个Code



    //所有客户端 表,!!!其实这里可以用QList,因为pIOCPSocketContext的ExtraData就是pClientInfo!!!
    //QMap速度快!!!用来客户查找客户
    //用SocketID作为Key
    QMap<qint32,_ClientInfo*>       m_mapAllClients;    //pIOCPSocketContext,pClientInfo
    //QList<_ClientInfo*> m_AllClientsList;    //pIOCPSocketContext,pClientInfo

    //QMap<SOCKET,_ClientInfo*> mapClientSocket;



    //所有 群组 表（!!!鹰：这个结构可以不用，直接用m_mapUsing也可!!!）
    QMap<QUuid,_ClientsGroupInfo*>  m_mapGroups;

    _LMS_Pool<_ClientsGroupInfo>    m_poolClientsGroupInfo;    //Group的池
    //QThreadPool m_ThreadPool;


    Config                  m_ConfigFile;    //配置文件
    QVariantMap             m_vmapConfig;
    //QSqlDatabase db;


//回调函数和参数
public:
//JoinGroup的回调函数，限制哪些不能加入(返回false不能加入)
    void                    *m_pJoinGroupCallBackParam;
    bool                    (*m_fnJoinGroupCallBack)(_ClientInfo*, _ClientsGroupInfo*, void*);


//私有数据成员
protected:

    QFile                   m_fileLog;         //日志
    QTextStream             m_txtstreamLog;    //日志


};

Q_DECLARE_OPERATORS_FOR_FLAGS(_ClientsGroupInfo::_GroupStatusFlags)  //写在全局

#endif // SERVER_H
